/**
 * Oracle's implementation for watching a directory
 * made it to be runnable so it can be run in a separate thread
 * and added functionality for it to access DB and tag files using supplied
 * algorithms
 * @author Shinichi.
 */
package System;
/*
 * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Oracle nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

import java.nio.file.*;
import static java.nio.file.StandardWatchEventKind.*;
import static java.nio.file.LinkOption.*;
import java.nio.file.attribute.*;
import java.io.*;
import java.util.*;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;

import DataTags.DataTagsAdapter;
import DataTags.files;
import Factory.SFac;
import Main.main;

/**
 * Example to watch a directory (or tree) for changes to files.
 */

public class WatchDir extends Thread {
	DataTagsAdapter DTA=main.menu.search.getDataTagsAdapter();
    private final WatchService watcher;
    private final Map<WatchKey,Path> keys;
    private final boolean recursive;
    private boolean trace = false;
    private LinkedList<Classifier> myClassifiers=null;
	private String[] exp;
    @SuppressWarnings("unchecked")
    static <T> WatchEvent<T> cast(WatchEvent<?> event) {
        return (WatchEvent<T>)event;
    }

    /**
     * Register the given directory with the WatchService
     */
    private void register(Path dir) throws IOException {
        WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
        if (trace) {
            FileRef prev = keys.get(key);
            if (prev == null) {
                System.out.format("register: %s\n", dir);
            } else {
                if (!dir.equals(prev)) {
                    System.out.format("update: %s -> %s\n", prev, dir);
                }
            }
        }
        keys.put(key, dir);
    }

    /**
     * Register the given directory, and all its sub-directories, with the
     * WatchService.
     */
    private void registerAll(final Path start) throws IOException {
        // register directory and sub-directories
        Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult preVisitDirectory(Path dir) {
                try {
                    register(dir);
                } catch (IOException x) {
                    throw new IOError(x);
                }
                return FileVisitResult.CONTINUE;
            }
        });
    }

    /**
     * Creates a WatchService and registers the given directory
     * Added:a boolean passed by creator that if we change it to true it will stop the runing
     * 		 reference to list of classifiers
     */
    public WatchDir(String[] exp,LinkedList<Classifier> classifiers,Path dir, boolean recursive) throws IOException {
        this.watcher = FileSystems.getDefault().newWatchService();
        this.keys = new HashMap<WatchKey,Path>();
        this.recursive = recursive;
        this.myClassifiers=classifiers;
        this.exp=exp;
        if (recursive) {
            System.out.format("Scanning %s ...\n", dir);
            registerAll(dir);
            System.out.println("Done.");
        } else {
            register(dir);
        }

        // enable trace after initial registration
        this.trace = true;
    }

    /**
     * Process all events for keys queued to the watcher
     * Added functionality:
     * 		on every added file updates the DB
     * 		on every deleted file/dir updates the DB
     * @author Shinichi 
     */
    @SuppressWarnings("unchecked")
	void processEvents() { 
        while(true){

            // wait for key to be signalled
            WatchKey key;
            try {
                key = watcher.take();
            } catch (InterruptedException x) {
                return;
            }

            Path dir = keys.get(key);
            if (dir == null) {
                System.err.println("WatchKey not recognized!!");
                continue;
            }

            for (WatchEvent<?> event: key.pollEvents()) {
                WatchEvent.Kind kind = event.kind();

                // TBD - provide example of how OVERFLOW event is handled
                if (kind == OVERFLOW) {
                    continue;
                }

                // Context for directory entry event is the file name of entry
                WatchEvent<Path> ev = cast(event);
                Path name = ev.context();
                Path child = dir.resolve(name);

                // print out event
                System.out.format("%s: %s\n", event.kind().name(), child);
                if(kind== ENTRY_MODIFY){
                	//filter them
                }
                if(kind== ENTRY_CREATE){
                	//new file was added need to autotag it if it's not dir
                	try {
						if (!Attributes.readBasicFileAttributes(child, NOFOLLOW_LINKS).isDirectory()){
							File toput=new File(child.toString());
							if(toput.exists()){
								System.out.println("file Ok!\n");
							}
							//put the file into the DB
							DTA.addFile(toput);
							//won't autotag if we told it to stop
							//won't autotag files that don't match regs for this source
							for (int i = 0; i < myClassifiers.size(); i++) {
								for (String ins : exp) {
									if(ins.contains("*")){//there is wildcard
										String nins=ins.replace('*',' ');
										nins=nins.replaceFirst(" ", "");
										System.out.println("exp: "+nins);
										System.out.println("name: "+toput.getName());
										if(toput.getName().contains(nins)){
												main.menu.log.update("new: "+toput.getName()+" at: "+toput.getPath());
												DTA.addTagsTo(toput.getName(), 
														Long.toString(toput.lastModified()),
														toput.getPath(),
														myClassifiers.get(i).getAlgorithmInts().getTags(toput));
												break;
										}
									}
									else{//no wildcard
										if (toput.getName().equals(ins)) {
											main.menu.log.update("new: "+toput.getName()+" at: "+toput.getPath());
											DTA.addTagsTo(toput.getName(), Long.toString(toput.lastModified()),toput.getPath(),myClassifiers.get(i).getAlgorithmInts().getTags(toput));
											break;	
										}
									}
								}
							}
						}
					} catch (Exception e) {e.printStackTrace();}
                }
                if(kind== ENTRY_DELETE){
                	//file removed need to remove it and its tags from db
					String a=child.toString().replace('\\', '%');
					a=a.replace('\'','_');
					String q="SELECT f FROM files f WHERE f.path LIKE '"+a+"%'";
					System.out.println(q);
					SessionFactory factory=SFac.get();
					Session session=factory.openSession();
					Query query=session.createQuery(q);
					List<files> fList=query.list();
					//delete all the files from the list we got form query
					for (files files : fList) {
						System.out.println(files.getPath());
						File todel= new File(files.getPath());
						main.menu.log.update(todel.getName()+" at: "+todel.getPath()+" was deleted");
						DTA.deleteFile(todel);
					}
					session.flush();
					session.close();
                }
                // if directory is created, and watching recursively, then
                // register it and its sub-directories
                if (recursive && (kind == ENTRY_CREATE)) {
                    try {
                        if (Attributes.readBasicFileAttributes(child, NOFOLLOW_LINKS).isDirectory()) {
                            registerAll(child);
                        }
                    } catch (IOException x) {
                        // ignore to keep sample readbale
                    }
                }
            }

            // reset key and remove from set if directory no longer accessible
            boolean valid = key.reset();
            if (!valid) {
                keys.remove(key);

                // all directories are inaccessible
                if (keys.isEmpty()) {
                    break;
                }
            }
        }
        
	}

    /**
     * 
     */
    static void usage() {
        System.err.println("usage: java WatchDir [-r] dir");
        System.exit(-1);
    }
	/**
	 * @see java.lang.Thread#run()
	 */
	public void run() {
		processEvents();
	}
	/**
	 * Terminates the thread
	 */
	public void terminate(){
		this.stop();
	}
}

